package br.org.groupware_workbench.collabElement.coop.recommendMgr.api;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import br.org.groupware_workbench.collabElement.coop.recommendMgr.internal.RecommendDAO;
import br.org.groupware_workbench.collabElementFw.facade.CollabElementInstance;
import br.org.groupware_workbench.collabletFw.facade.CollabletInstance;
import br.org.groupware_workbench.coreutils.DAOFactory;
import br.org.groupware_workbench.coreutils.GenericEntity;

/**
 * Fachada para acesso ao componente
 */
public class RecommendMgrInstance extends CollabElementInstance {

    private RecommendDAO recommendDAO = DAOFactory.get(RecommendDAO.class);
    private int numRelated = 5; // TODO: Não é thread-safe.

    public RecommendMgrInstance() {
        super();
    }

    @Override
    public void destroy() {
        this.recommendDAO.deleteByIdInstance(this.getId());
    }

    // --------------------- Buscas ----------------------

    public Collection<Recommend> listAll() {
        return this.recommendDAO.listByIdInstance(this.getId());
    }

    // --------------------- Operações ----------------------

    public void saveRecommend(Recommend recommend) {
        recommend.setIdInstance(this.getId());
        this.recommendDAO.save(recommend, recommend.getId() == 0);
    }

    public void delete(Long idRecommend) {
        this.recommendDAO.deleteById(idRecommend);
    }

    public static Collection<Recommend> listAll(RecommendMgrInstance instance) {
        return instance.listAll();
    }

    public RecommendDAO getRecommendDAO() {
        return recommendDAO;
    }

    public int getNumRelated() {
        return numRelated;
    }

    public void setNumRelated(int numRelated) {
        this.numRelated = numRelated;
    }

    public List<Recommend> getRelated(GenericEntity genericEntity) {
        long idGenericEntity = genericEntity.getId(); 
        List<Recommend> relatedList = recommendDAO.listGenericEntityByNumRelated(this.getId(), genericEntity, getNumRelated());
        List<Recommend> related = new ArrayList<Recommend>();

        for (Recommend recommend : relatedList){
            if(idGenericEntity == recommend.getGenericEntityTo().getId()){
                GenericEntity tempGeneric = recommend.getGenericEntityTo();
                recommend.setGenericEntityTo(recommend.getGenericEntityFrom());
                recommend.setGenericEntityFrom(tempGeneric);
            }
            related.add(recommend);
        }
        return related;
    }

    // TODO verificar parametro não utilizado!
    public void generateRelated(CollabletInstance collabletMgr, List<GenericEntity> genericsEntity , GenericEntity genericEntityFrom){
        List<Recommend> relatedList = new ArrayList<Recommend>();

        for (GenericEntity genericTo : genericsEntity) {
            if (!genericEntityFrom.equals(genericTo)) {
                Recommend recommend = new Recommend(genericEntityFrom, genericTo);
                recommend.setIdInstance(this.getId());
                if (relatedList.size() < getNumRelated()) {
                    relatedList.add(recommend);
                } else {
                    relatedList = getListMostRelated(relatedList, recommend);
                }
            }
        }
        for (Recommend rec : relatedList){
            recommendDAO.save(rec, true);
        }
    }

    public void deleteAll() {
        this.recommendDAO.deleteByField("idInstance", this.getId());
    }

    // TODO otimizar o algoritmo.
    public <E extends GenericEntity> void calculeAll(List<E> currentList) {
        List<E> toList = currentList;
        Recommend recommend = new Recommend();
        int i = 1;

        for (E from : currentList) {
            List<Recommend> relatedList = new ArrayList<Recommend>();
            for (int j = i ; j < toList.size(); j++) {
                E to = toList.get(j); // TODO: Certificar-se que o acesso é garantidamente não sequencial.
                if (!from.equals(to)) {
                    recommend = new Recommend(from, to);
                    recommend.setIdInstance(this.getId());
                    if (relatedList.size() < getNumRelated())  {
                        relatedList.add(recommend);
                    } else {
                        relatedList = getListMostRelated(relatedList, recommend);
                    }
                }
            }
            for (Recommend rec : relatedList){
                recommendDAO.save(rec, true);
            }
            i++;
            System.out.println("num de registro : " + i);
        }
    }

    public List<Recommend> getListMostRelated(List<Recommend> relatedList, Recommend recommend){

        double smaller = relatedList.get(0).getSimilaritysProduct();
        int indexSmaller = 0;

        for (int i = 1; i < relatedList.size(); i++) {
            if (smaller > relatedList.get(i).getSimilaritysProduct()) {
                smaller = relatedList.get(i).getSimilaritysProduct();
                indexSmaller = i;
            }
        }
        if (smaller < recommend.getSimilaritysProduct()) {
            relatedList.remove(indexSmaller);
            relatedList.add(indexSmaller, recommend);
        }

        return relatedList;
    } 
}
